import { Provide } from "@midwayjs/decorator";
import { LogsContent, LogsCacheData, LogsData, QueryLogsParam } from '../interface'
import { InjectEntityModel } from '@midwayjs/typegoose';
import { ReturnModelType } from '@typegoose/typegoose';
import { Logs } from '../entity/logs';
import { Tree } from '../entity/tree';
import queue from 'queue';
import LRUCache = require("lru-cache");
import mongoConf from '../config/config.mongo';

const options = {
    max: 500,
    // maxSize: 5000,
    // ttl: 1000 * 60 * 5,
}
const logsChache = new LRUCache(options)

const saveLosQueue = new queue({ autostart: true, concurrency: 1 });

@Provide()
export class LoggerHanderService {

    @InjectEntityModel(Logs)
    logsModel: ReturnModelType<typeof Logs>; // 返回 一个 Logs 集合类型

    @InjectEntityModel(Tree)
    treeModel: ReturnModelType<typeof Tree>; // 返回 一个 Tree 集合类型

    // 存日志
    async restoreLog(logsContent: LogsContent): Promise<boolean> {

        this.saveInCache(logsContent);
        return true;
    }

    // 先缓存 满n条
    private saveInCache(logsContent: LogsContent): void {

        const logsKey = logsContent.projectName + logsContent.category.join('_') + logsContent.type;
        console.log(logsKey);

        // 存到相应项目
        let logsCacheData: LogsCacheData = logsChache.has(logsKey) ? logsChache.get(logsKey) : { content: [] };
        try {
            logsChache.set(logsKey,
                {
                    project_name: logsContent.projectName,
                    category: logsContent.category.join('_'),
                    time: logsCacheData.content.length ? logsCacheData.time : logsContent.time,
                    type: logsContent.type,
                    logs_name: logsContent.logsName,
                    content: [
                        ...logsCacheData.content,
                        {
                            time: logsContent.time,
                            row: logsContent.row,
                            filePath: logsContent.filePath,
                            logs: logsContent.logsContent,
                        }
                    ]
                }
            )
        } catch (_) {
            console.log(_);
        }

        // 判断长度 > mongoKesContentNum 阈值 放到队列
        if (logsCacheData.content.length == mongoConf.mongoKesContentNum - 1) {

            let uploadData = logsChache.get(logsKey);
            saveLosQueue.push((async cb => {
                // 上传 日志信息
                // await this.uploadLogs({});
                // 维护 日志列表树状结构
                await this.updateTreeStruct(uploadData);
                cb();
            }))

            // 清空此缓存
            logsChache.delete(logsKey);
            // console.log('缓存总大小: ', logsChache);
        }
    }

    // private async uploadLogs(logsInfo: LogsCacheData): Promise<boolean> {

    //     try {
    //         const { _id: id } = await this.logsModel.create(logsInfo as Logs);

    //         console.log(id);
    //         return true;
    //     } catch (_) {
    //         console.log(_);
    //         return false;
    //     }
    // }

    // 更新此项目 tree节点结构
    private async updateTreeStruct(logsContent: LogsCacheData): Promise<boolean> {
        try {
            const projectInfo = await this.treeModel.findOne(
                {
                    label: logsContent.project_name
                }
            );

            // 新增 or 更新
            if (!projectInfo) {
                await this.createTree(logsContent);
            } else {
                await this.updateTree(projectInfo, logsContent);
            }
            return true;
        } catch (_) {
            console.log(_);
            return false;
        }
    }

    /**
     * 新增 tree
     * @param logsContent
     * @returns
     */
    private async createTree(logsContent: LogsCacheData): Promise<boolean> {
        try {
            let { _id: id } = await this.treeModel.create({
                label: logsContent.project_name,
                attr: 'project_name',
                children: [
                    logsContent.category ? logsContent.category.split('_').reduce((pre, cur) => {
                        return {
                            ...pre,
                            children: [
                                {
                                    label: cur,
                                    attr: 'category',
                                    children: []
                                }
                            ]
                        }
                    }, {
                        label: logsContent.category.split('_')[0],
                        attr: 'category',
                        children: []
                    })
                        : {
                            label: logsContent.logs_name,
                            attr: 'logs_name',
                        }
                ]
            });
            console.log(id);
            return true;
        } catch (_) {
            console.log(_);
            return false
        }
    }

    /**
     * 更新tree
     * @param logsContent
     * @returns boolean
     */
    private async updateTree(projectInfo, logsContent: LogsCacheData): Promise<boolean> {
        try {

            let newsProjectInfo = projectInfo;

            // 看原节点是否有分类目录
            if (logsContent.category.length) {

                let newsCategory;
                let categoryArr = logsContent.category.split('_');
                let info = newsProjectInfo.children;
                // 原节点没有此分类
                for (let pos = 0; pos < categoryArr.length; pos++) {
                    const item = categoryArr[pos];
                    // 插入对应位置
                    if (info.every(folder => folder.label !== item)) {
                        //
                        newsCategory = categoryArr.slice(pos).reduce((pre, cur, index) => {
                            return {
                                ...pre,
                                children: [
                                    {
                                        label: cur,
                                        attr: 'category',
                                        // 最后一项，子是日志文件名
                                        children: index == categoryArr.length - 1 ? [
                                            {
                                                label: logsContent.logs_name,
                                                attr: 'logs_name',
                                            }
                                        ] : []
                                    }
                                ]
                            }
                        }, {
                            label: categoryArr[pos],
                            attr: 'category',
                            children: []
                        })

                        // 插入新结构
                        info.push(newsCategory);
                        break;
                    };
                    // 继续找
                    info = info.filter(folder => folder.label == item)[0].children;
                    // 最后一项 --> 有此分类所有目录
                    if (pos == categoryArr.length - 1) {
                        // 无此日志文件  新增
                        if (info.every(item => item.label !== logsContent.logs_name)) {
                            info.push({
                                label: logsContent.logs_name,
                                attr: 'logs_name',
                            })
                        }
                    }
                }
                // 更新
                await this.treeModel.updateOne({ label: projectInfo.label }, newsProjectInfo);
                return true;
            }

            // 无分类目录
            if (newsProjectInfo.children.every(item => item.label !== logsContent.logs_name)) {
                console.log('1111');
                
                newsProjectInfo.children.push({
                    label: logsContent.logs_name,
                    attr: 'logs_name',
                })

                // 更新
                await this.treeModel.updateOne({ label: projectInfo.label }, newsProjectInfo);
            }

        } catch (_) {
            console.log(_);
            return false;
        }
    }

    /**
     * 获取日志列表
     * @param params QueryLogsParam
     * @returns
     */
    async getLogs(params: QueryLogsParam): Promise<Array<LogsData> | boolean> {

        const { start_time, end_time } = params;
        let query: any = {
            project_name: params.project_name,
            category: params.category,
            type: params.type,
            logs_name: params.logs_name,
        }

        if (start_time && end_time) {
            query.time = { $lt: end_time, $gt: start_time }
        }

        try {  

            const logsList = await this.logsModel.find(query).sort({ _id: 1 }).limit(params.limit / mongoConf.mongoKesContentNum).skip(params.offset / mongoConf.mongoKesContentNum);
            if (!logsList.length) {
                return [];
            }

            return  logsList.reduce((pre, cur) => [...pre, ...cur.content], []);
        } catch (_) {
            console.log(_);
            return false;
        }
    }
}